package net.xdclass.service.stress.core;

import lombok.extern.slf4j.Slf4j;
import net.xdclass.dto.ReportDTO;
import net.xdclass.dto.stress.CSVDataFileDTO;
import net.xdclass.exception.BizException;
import net.xdclass.model.StressCaseDO;
import net.xdclass.service.common.FileService;
import net.xdclass.service.common.ResultSenderService;
import net.xdclass.service.common.impl.KafkaSenderServiceImpl;
import net.xdclass.util.FileUtil;
import net.xdclass.util.JsonUtil;
import org.apache.jmeter.JMeter;
import org.apache.jmeter.config.CSVDataSet;
import org.apache.jmeter.save.SaveService;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.SearchByClass;
import org.springframework.context.ApplicationContext;

import java.io.File;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

@Slf4j
public class StressJmxEngine extends BaseStressEngine {

    public StressJmxEngine(StressCaseDO stressCaseDO, ReportDTO reportDTO, ApplicationContext applicationContext) {
        this.stressCaseDO = stressCaseDO;
        this.reportDTO = reportDTO;
        this.applicationContext = applicationContext;
    }

    //组装测试计划
    @Override
    public void assembleTestPlan() {
        File jmxFile = null;
        HashTree testPlanHashTree = null;

        try {
            //创建了一个临时文件，然后读取远程文件内容，写入到临时文件中
            //打印出临时文件，看看格式是否正确
            jmxFile = File.createTempFile("jmeter-script", ".jmx");
            try (FileWriter fileWriter = new FileWriter(jmxFile)) {
                //读取远程文件的内容，写到本地文件
                FileService minioService = getApplicationContext().getBean(FileService.class);
                String tempAccessFileUrl = minioService.getTempAccessFileUrl(stressCaseDO.getJmxUrl());
                String jmxContent = FileUtil.readRemoteFile(tempAccessFileUrl);
                fileWriter.write(jmxContent);
            }

            //加载测试计划树，jmx脚本
            testPlanHashTree = SaveService.loadTree(jmxFile);

            //转换测试计划树，这里会报错！！！！！！！！！！！！！！
            JMeter.convertSubTree(testPlanHashTree, false);

            //获取自定义结果收集器
            ResultSenderService resultSenderService = getApplicationContext().getBean(KafkaSenderServiceImpl.class);
            EngineSampleCollector engineSampleCollector = super.getEngineSampleCollector(resultSenderService);
            testPlanHashTree.add(testPlanHashTree.getArray()[0], engineSampleCollector);

            //处理参数化 TODO
            parseParamFilesToScript(testPlanHashTree);

        } catch (Exception e) {
            log.error("异常信息{}", e.getMessage());
            throw new RuntimeException("组装测试计划失败");
        } finally {
            //删除临时文件
            if (jmxFile != null) {
                boolean flag = jmxFile.delete();
                log.info("临时本地jmx文件路径：{}", jmxFile.getAbsolutePath());
                if (!flag) {
                    log.error("删除临时文件失败");
                }
            }
        }

        super.setTestPlanHashTree(testPlanHashTree);
    }

    /**
     * 获取远程的参数文件
     * 解析relation字段映射到DTO
     * 对jmx脚本进行遍历
     * 提取元素是csvdataset对象到容器里面
     * 根据名称匹配对应的路径，修改fileName属性
     * @param testPlanTree
     */
    public void parseParamFilesToScript(HashTree testPlanTree) {
        //使用当前类的applicationContext获取FileService
        FileService fileService = applicationContext.getBean(FileService.class);

        //获取映射字段，使用当前类的stressCaseDO,csvDataFileDTOS是从stressCaseDO中获取的
        String relation = stressCaseDO.getRelation();
        List<CSVDataFileDTO> csvDataFileDTOS = JsonUtil.json2List(relation, CSVDataFileDTO.class);

        //定义一个容器List，存储CSVDataSet
        List<CSVDataSet> csvDataSetList = new ArrayList<>();

        //jmx脚本进行遍历，遍历testPlanTree，并把测试计划树中的元素都存储到csvDataSetList
        SearchByClass<TestElement> testElementVisitor = new SearchByClass<>(TestElement.class);
        testPlanTree.traverse(testElementVisitor);
        Collection<TestElement> searchResults = testElementVisitor.getSearchResults();

        //提取jmx脚本里面的csv配置类，存储到list
        for(TestElement testElement : searchResults){
            if(testElement instanceof CSVDataSet csvDataSet){
                boolean enabled = csvDataSet.getProperty("TestElement.enabled").getBooleanValue();
                if(enabled){
                    //存储CSVDataSet
                    csvDataSetList.add(csvDataSet);
                }
            }
        }

        //通过列表遍历jmx中的csv数据集，然后根据filename进行关联
        for(CSVDataSet csvDataSet : csvDataSetList){
            String csvDataSetSlotName = csvDataSet.getProperty("TestElement.name").getStringValue();
            if(csvDataFileDTOS!=null){
                for(CSVDataFileDTO csvDataFileDTO : csvDataFileDTOS){
                    //从stressCaseDO获取的信息与从testPlanTree获取的信息相同
                    if(Objects.equals(csvDataSetSlotName,csvDataFileDTO.getName())){
                        //从minIO读取对应的文件，然后把文件名与csvDataSet关联起来，csvDataSet是从testPlanTree获取的
                        String localTempFile = fileService.copyRemoteFileToLocalTempFile(csvDataFileDTO.getRemoteFilePath());
                        csvDataSet.setProperty("filename",localTempFile);
                    }
                }
            }
        }
    }
}
